/* SSTV PD90 mode bitmap encoder using a VS1003-based audio module
 * (Jaycar: XC4516)
 * --
 * Chris Baird, July 2022 <vk2cjb@gmail.com>
 * Usage as per the GPLv3.
 */
/* based on vs1003_hello created 2012-04-05 by Andrey Karpov
 *
 * VS1003 development board connected thusly to the Arduino Mega:
 * [GND, 5V]
 * /DCS = 46
 * DREQ = 47
 * /RES = 48
 *  /CS = 49
 *   SI = 51
 *   SO = 50
 * SCLK = 52
 */

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <Arduino.h>
#include <SPI.h>
#include "VS1003.h"
#include "sine.h"
#include "image.h"

VS1003 player(49, 46, 47, 48); // cs_pin, dcs_pin, dreq_pin, reset_pin

#define samplebytes (2)
#define channels (1)
#define samplerate (8000)
#define datarate (samplerate*channels*samplebytes)

#define TONESIZE (16)	      // using anything else causes distortion
uint16_t tonebuf[TONESIZE];
uint16_t freqlookup[256];
#define VISCODE (99)		// PD90
#define scanlen (1362)		// also

const uint8_t wavheader[] =
  {
   'R','I','F','F',
   0,0,0,0,			// wav size (0=play indefinitely)
   'W','A','V','E', 'f','m','t',' ',
   16, 0, 0, 0,			// fmt_chunk_size
   1, 0,			// audio format = PCM
   1, 0,			// num_channels
   samplerate&255, (samplerate>>8)&255, samplerate>>16,0,
   datarate&255, (datarate>>8)&255, datarate>>16,0,
   samplebytes, 0,		// sample_alignment
   samplebytes<<3, 0,
   'd','a','t','a',
   0,0,0,0			// data size (0=infinite)
  };

// ----------------------------------------------------------------------

void setup ()
{
  SPI.begin();
  player.begin();
  player.setVolume(0);
  delay(4500);			// hack to fix power-up drifting
  player.playChunk(wavheader,sizeof(wavheader));

  for (int i = 0; i < 256; i++)
    freqlookup[i] = 1500.0 + ((800.0 * (float)i) / 255.0);
}

// ----------------------------------------------------------------------

void sampleout (uint16_t freq)
{
  static float iphase = 0;
  static int bp = 0;

  iphase += ((float)SINESIZE*(float)freq) / (float)samplerate;
  iphase = fmodf(iphase,SINESIZE); // ..could be a &255, really..
  tonebuf[bp] = pgm_read_word(&sine[(int)iphase]); // _far & etc wouldn't work
  if (++bp == TONESIZE)
    {
      player.playChunk((const uint8_t*)tonebuf,sizeof(tonebuf));
      bp = 0;
    }
}


void tonegen (float freq, uint16_t samples)
{
  while (samples--) sampleout(freq);
}

// ----------------------------------------------------------------------

void vis(uint8_t c)
{
  uint16_t i, p = 0;

  tonegen(1900,2400);		// 300ms @ 8kHz = 2400 samples
  tonegen(1200,80);		// 10ms
  tonegen(1900,2400);		// 300ms
  tonegen(1200,240);		// 30ms

  for (i = 1; i != 256; i <<= 1)
    {
      if ((i == 128) && (p & 1)) c |= 128;
      if (c & i)
	{
	  p++;
	  tonegen(1100,240);
	}
      else tonegen(1300,240);
    }
  tonegen(1200,240);
}

// ----------------------------------------------------------------------

void doline(uint32_t array, int y)
{
#define scanwidth (scanlen*256L)
  uint32_t p = array + y*320;
  uint32_t tf = scanwidth, fq = 1900;

  for (uint16_t x = 0; x < scanlen; x++)
    {
      if (tf >= scanwidth)
	{
	  tf -= scanwidth;
	  fq = freqlookup[pgm_read_byte_far(p++)];
	}
      tf += 320L << 8;
      sampleout(fq);
    }
}

// ----------------------------------------------------------------------

void loop()
{
  uint32_t l0 = pgm_get_far_address(image_y0);
  uint32_t l1 = pgm_get_far_address(image_y1);
  uint32_t r = pgm_get_far_address(image_cr0);
  uint32_t b = pgm_get_far_address(image_cb0);

  tonegen(1000,samplerate);	// trigger VOX
  tonegen(0,samplerate>>4);
  vis(VISCODE);

  for (int y = 0; y < 256; y += 2)
    {
      int ry = (y & 127) >> 1;

      if (y == 128)
	{
	  l0 = pgm_get_far_address(image_y2);
	  l1 = pgm_get_far_address(image_y3);
	  r = pgm_get_far_address(image_cr1);
	  b = pgm_get_far_address(image_cb1);
	}

      tonegen(1200,160);
      tonegen(1500,17);

      doline(l0,ry);
      doline(r,ry);
      doline(b,ry);
      doline(l1,ry);
    }

  tonegen(0,samplerate);
  delay(30000);
}

// ----------------------------------------------------------------------
